#include "myTCP.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#include "cJSON.h"
#include "myTimer.h"

#define PORT 6666
#define KEEPALIVE_IDLE 5
#define KEEPALIVE_INTERVAL 5
#define KEEPALIVE_COUNT 4
#define CLIENT_NUM_MAX 2

static const char *TAG = "myTCP";

tcp_sock_t *tcp_socks = NULL;
uint16_t client_num = 0;

// 创建设备链表
tcp_sock_t *create_tcp_client_list(void)
{
  tcp_sock_t *list = (tcp_sock_t *)malloc(sizeof(tcp_sock_t));
  list->next = NULL;
  list->name = NULL;
  return list;
}

// 新增设备
void add_tcp_client(uint32_t sock)
{
  tcp_sock_t *new_client = (tcp_sock_t *)malloc(sizeof(tcp_sock_t));
  new_client->sock = sock;
  new_client->label = 0;
  new_client->ready = 0;
  new_client->integaral = INTEGARAL_MAX;
  new_client->name = NULL;
  new_client->next = tcp_socks->next;
  tcp_socks->next = new_client;
}

void update_tcp_client_name(uint32_t sock, const char *name)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (move->sock == sock)
    {
      if (move->name == NULL)
      {
        move->name = (char *)malloc(strlen(name));
        strcpy(move->name, name);
      }
      break;
    }
    move = move->next;
  }
}
// 删除设备
uint8_t del_tcp_client(uint32_t sock)
{
  tcp_sock_t *move_f = tcp_socks;
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (move->sock == sock)
    {
      move_f->next = move->next;
      if (move->name)
        free(move->name);
      free(move);
      return 0;
    }
    move_f = move;
    move = move->next;
  }
  return 1;
}

// 设备准备
void ready_tcp_client(int sock)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (move->sock == sock)
    {
      move->ready = 1;
      break;
    }
    move = move->next;
  }
}

// 检查所有设备是否都准备
uint8_t is_all_ready(void)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (move->ready != 1)
    {
      return 0;
    }
    move = move->next;
  }
  move = tcp_socks->next;
  while (move)
  {
    move->ready = 0;
    move = move->next;
  }
  return 1;
}

// 设备上报积分
void report_tcp_client(int sock, double integaral)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (move->sock == sock)
    {
      move->ready = 2;
      move->integaral += integaral;
      break;
    }
    move = move->next;
  }
}

// 对所有设备排序，名次赋值给标志位
void sort_client(void)
{
  tcp_sock_t *move = tcp_socks->next;
  // 先清空名次
  while (move)
  {
    move->rank = 0;
    move = move->next;
  }
  // 开始排名
  double integaral_max = 0;
  for (uint16_t i = 1; i <= client_num; i++){
    // 找到最大名次的积分
    move = tcp_socks->next;
    while(move){
      if(move->rank == 0 && move->integaral > integaral_max){
        integaral_max = move->integaral;
      }
      move = move->next;
    }
    move = tcp_socks->next;
    // 对未赋名次的设备中最大积分的赋予名次
    while(move){
      if(move->rank == 0 && move->integaral == integaral_max){
        move->rank = i;
        break;
      }
      move = move->next;
    }
  }
}
// 检查所有设备是否都上报积分
uint8_t is_all_report(void)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (move->ready != 2)
    {
      return 0;
    }
    move = move->next;
  }
  move = tcp_socks->next;
  while (move)
  {
    move->ready = 0;
    move = move->next;
  }
  return 1;
}

// tcp send data to client for robust implementation.
static void tcp_robust_send(int sock, const uint8_t *data, size_t len)
{
  // send() can return less bytes than supplied length.
  int to_write = len;
  while (to_write > 0)
  {
    int written = send(sock, data + (len - to_write), to_write, 0); // ？
    if (written < 0)
    {
      ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
      break;
    }
    to_write -= written; // to_write = 0
  }
}

// 向所有 TCP 客户端发送广播
void tcp_broadcast(tcp_msg_t *msg)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    // send()可能一次发不完，为了程序健壮性，确保数据全部发完
    tcp_robust_send(move->sock, msg->buf, msg->len);
    move = move->next;
  }
}

// 向指定 TCP 客户端发送消息
void tcp_send(tcp_msg_t *msg, int sock)
{
  // send()可能一次发不完，为了程序健壮性，确保数据全部发完
  tcp_robust_send(sock, msg->buf, msg->len);
}

// 向除了指定 TCP 客户端发送消息
void tcp_send_either(tcp_msg_t *msg, int sock)
{
  tcp_sock_t *move = tcp_socks->next;
  while (move)
  {
    if (sock != move->sock)
    {
      tcp_robust_send(move->sock, msg->buf, msg->len);
    }
    move = move->next;
  }
}

void handle_client_task(void *pt)
{
  int len;
  char rx_buffer[128]; // 接收的数据
  int sock = (int)pt;  // 创建任务传过来的参数（sock句柄）
  int connecttag;      // 断开连接结束整个任务的标记
  add_tcp_client(sock);
  cJSON *json_parsed = NULL;
  while (1)
  {
    do
    {
      len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); // 接收(有数据时>0)
      if (len < 0)
      {
        ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
      }
      else if (len == 0)
      {
        ESP_LOGW(TAG, "Connection closed");
        del_tcp_client(sock);
        shutdown(sock, 0);
        close(sock);
        connecttag = 1;
      }
      else
      {
        rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string将接收到的任何内容终止为空，并将其视为字符串
        ESP_LOGI(TAG, "TCP Received %d bytes: %s", len, rx_buffer);
        json_parsed = cJSON_ParseWithLength(rx_buffer, len);
        static cJSON *username = NULL;
        static cJSON *messageType = NULL;
        static cJSON *payload_out = NULL;
        static cJSON *rankingList = NULL;
        static cJSON *rankingList_arr[CLIENT_NUM_MAX] = {NULL};
        static cJSON *json_parsed_out = NULL;
        static tcp_msg_t msg_out;
        if (cJSON_GetArraySize(json_parsed))
        {
          ESP_LOGI(TAG, "Received JSON pack");
          /* username */
          if (cJSON_HasObjectItem(json_parsed, "username") &&
              cJSON_IsString(cJSON_GetObjectItem(json_parsed, "username")))
          {
            username = cJSON_GetObjectItem(json_parsed, "username");
            update_tcp_client_name(sock, cJSON_GetStringValue(username));
            ESP_LOGI(TAG, "username: %s", cJSON_GetStringValue(username));
          }
          else
          {
            ESP_LOGE(TAG, "ERROR in \"username\"");
            goto DeleteJSON;
          }
          /* messageType */
          if (cJSON_HasObjectItem(json_parsed, "messageType") &&
              cJSON_IsString(cJSON_GetObjectItem(json_parsed, "messageType")))
          {
            messageType = cJSON_GetObjectItem(json_parsed, "messageType");
            ESP_LOGI(TAG, "messageType: %s", cJSON_GetStringValue(messageType));
            if (strcmp("1", cJSON_GetStringValue(messageType)) == 0)
            {
              ready_tcp_client(sock);
              if (is_all_ready())
              {
                // TODO 游戏开始
                game_start_counting();
              }
            }
            else if (strcmp("2", cJSON_GetStringValue(messageType)) == 0 &&
                     cJSON_HasObjectItem(json_parsed, "payload"))
            {
              json_parsed_out = cJSON_CreateObject();
              cJSON_AddStringToObject(json_parsed_out, "messageType", "4");
              cJSON_AddItemReferenceToObject(json_parsed_out, "payload", cJSON_GetObjectItem(json_parsed, "payload"));
              msg_out.buf = (uint8_t *)cJSON_Print(json_parsed_out); // 在下文释放内存
              msg_out.len = strlen((char *)(msg_out.buf));
              tcp_send_either(&msg_out, sock);
              free(msg_out.buf);
              msg_out.buf = NULL;
              cJSON_Delete(json_parsed_out);
              json_parsed = NULL;
            }
            else if (strcmp("3", cJSON_GetStringValue(messageType)) == 0 &&
                     cJSON_HasObjectItem(json_parsed, "payload") &&
                     cJSON_HasObjectItem(cJSON_GetObjectItem(json_parsed, "payload"), "integaral"))
            {
              ESP_LOGI(TAG, "integaral=%lf", cJSON_GetNumberValue(cJSON_GetObjectItem(cJSON_GetObjectItem(json_parsed, "payload"), "integaral")));
              report_tcp_client(sock, cJSON_GetNumberValue(cJSON_GetObjectItem(cJSON_GetObjectItem(json_parsed, "payload"), "integaral")));
              if (is_all_report())
              {
                // TODO 发送排行榜
                json_parsed_out = cJSON_CreateObject();
                cJSON_AddStringToObject(json_parsed_out, "messageType", "5");
                payload_out = cJSON_AddObjectToObject(json_parsed_out, "payload");
                rankingList = cJSON_AddArrayToObject(payload_out, "rankingList");
                static tcp_sock_t *move;
                static uint16_t index;
                move = tcp_socks->next;
                index = 0;
                // TODO 排名
                sort_client();
                while (move)
                {
                  if (index >= client_num)
                    break;
                  cJSON_AddStringToObject(rankingList_arr[index], "username", move->name);
                  cJSON_AddNumberToObject(rankingList_arr[index], "rank", move->rank);
                  cJSON_AddNumberToObject(rankingList_arr[index], "integaral", move->integaral);
                  cJSON_AddItemToArray(rankingList, rankingList_arr[index]);
                  index++;
                  move = move->next;
                }
                msg_out.buf = (uint8_t *)cJSON_Print(json_parsed_out); // 在下文释放内存
                msg_out.len = strlen((char *)(msg_out.buf));
                tcp_broadcast(&msg_out);
                free(msg_out.buf);
                msg_out.buf = NULL;
                cJSON_Delete(json_parsed_out);
                json_parsed = NULL;
              }
            }
            else if (strcmp("4", cJSON_GetStringValue(messageType)) == 0)
            {
              game_stop_die();
              ESP_LOGI(TAG, "A car has died!");
            }
            else
            {
              ESP_LOGW(TAG, "Unknow \"messageType\" or error in \"payload\"");
              goto DeleteJSON;
            }
          }
          else
          {
            ESP_LOGE(TAG, "Error in \"messageType\"");
            goto DeleteJSON;
          }
        }
        else
        {
          ESP_LOGI(TAG, "Not JSON pack");
        }
      DeleteJSON:
        cJSON_Delete(json_parsed);
        json_parsed = NULL;
      }
    } while (len > 0); // 有数据接收就继续循环

    vTaskDelay(1000 / portTICK_PERIOD_MS);
    if (connecttag)
    {
      break; // 跳出最外面的while
    }
  }
  vTaskDelete(NULL); // 删除整个task，不然会触发看门狗
}

static void tcp_server_task(void *pvParameters)
{
  char addr_str[128];
  int addr_family = (int)pvParameters;
  int ip_protocol = 0;
  int keepAlive = 1;
  int keepIdle = KEEPALIVE_IDLE;
  int keepInterval = KEEPALIVE_INTERVAL;
  int keepCount = KEEPALIVE_COUNT;
  struct sockaddr_storage dest_addr;

  if (addr_family == AF_INET)
  {
    struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
    dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
    dest_addr_ip4->sin_family = AF_INET;
    dest_addr_ip4->sin_port = htons(PORT);
    ip_protocol = IPPROTO_IP;
  }

  int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
  if (listen_sock < 0)
  {
    ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
    vTaskDelete(NULL);
    return;
  }
  int opt = 1;
  setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

  ESP_LOGI(TAG, "Socket created");

  int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
  if (err != 0)
  {
    ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
    ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
    goto CLEAN_UP;
  }
  ESP_LOGI(TAG, "Socket bound, port %d", PORT);

  err = listen(listen_sock, 2);
  if (err != 0)
  {
    ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
    goto CLEAN_UP;
  }

  while (1)
  {

    ESP_LOGI(TAG, "Socket listening");

    struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
    socklen_t addr_len = sizeof(source_addr);
    int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
    if (sock < 0)
    {
      ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
      break;
    }

    // Set tcp keepalive option
    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
    // Convert ip address to string
    if (source_addr.ss_family == PF_INET)
    {
      inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
    }
    ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
    if (client_num < CLIENT_NUM_MAX)
      xTaskCreate(handle_client_task, "handle_client_task", 1024 * 4, (void *)sock, 5, NULL);
    else
    {
      ESP_LOGW(TAG, "Connection close, client is too much");
      shutdown(sock, 0);
      close(sock);
    }
  }

CLEAN_UP:
  close(listen_sock);
  vTaskDelete(NULL);
}

void TCP_server_init(void)
{
  tcp_socks = create_tcp_client_list();
  xTaskCreate(tcp_server_task, "tcp_server_task", 1024 * 4, (void *)AF_INET, 11, NULL);
}
